/**
 * 
 */
package com.code.google.p.jyve.berekeningen;

import java.awt.Color;

import com.code.google.p.jyve.database.Stof;

/** 
 * @author Boudewijn
 */
public interface Berekening {


	public static final int ZUUR = 0;
	public static final int BASE = 1;
	
	public static final double KW = 1e-14;//temperatuurafhankelijk
	
	/**
	 * De builder voor de berekeningen.
	 * Gebruik zoals bijv.
	 * 
	 * 	Berekening berekening = new Berekening.Builder(oplossing, titrant)
	 * 	.molariteitOplossing(molariteitOplossing)
	 * 	.molariteitTitrant(molariteitTitrant)
	 * 	.volumeOplossing(volumeOplossing)
	 * 	.color(Color.BLUE)
	 * 	.build();
	 * 
	 * @author Boudewijn
	 *
	 */
	public static final class Builder {
		
		private final Stof mOplossing;
		private final Stof mTitrant;
		private double mMolariteitOplossing = -1;
		private double mMolariteitTitrant = -1;
		private double mVolumeOplossing = -1;
		private Color mColor = Color.BLUE;
		
		/**
		 * De constructor voor deze Builder class.
		 * @param oplossing De stof die de oplossing vormt die voor deze berekening gebruikt wordt.
		 * @throws NullPointerException Als oplossing of titrant null is.
		 * @throws IllegalStateException Als titrant zowel zuur als base is.
		 * @throws IllegalStateException Als de waardigheid van de oplossing kleiner dan 1 is of groter dan 3.
		 * @param titrant De stof die de titrant vormt die voor deze berekening gebruikt wordt.
		 */
		public Builder(Stof oplossing, Stof titrant){
			if (oplossing == null) throw new NullPointerException("Oplossing mag niet null zijn.");
			if (titrant == null) throw new NullPointerException("Titrant mag niet null zijn.");
			int waardigheid = titrant.isBase() ? oplossing.getWaardigheidZuur() : oplossing.getWaardigheidBase();
			
			if ((waardigheid < 1) || (waardigheid > 3))
				throw new IllegalArgumentException("Oplossing heeft een waardigheid die niet is toegestaan." +
						"Namelijk: " + waardigheid);
			
			mOplossing = oplossing;
			if ((titrant.isBase()) && (titrant.isZuur()))
				throw new IllegalArgumentException("Titrant kan niet zowel zuur als base zijn.");
			mTitrant = titrant;
		}
		
		/**
		 * Hiermee bepaal je de molariteit van de oplossing voor deze berekening.
		 * @param molariteit De molariteit waarmee gerekend zal worden (niet optioneel).
		 * Deze moet bepaald worden, anders levert build een IllegalStateException.
		 * @throws IllegalArgumentException Als molariteit < 0. 
		 * @return Zichzelf om chainen toe te staan (zie documentatie class).
		 */
		public Builder molariteitOplossing(double molariteit){
			if (molariteit < 0) throw new IllegalArgumentException("Molariteit < 0");
			mMolariteitOplossing = molariteit;
			return this;
		}
		
		/**
		 * Hiermee bepaal je de molariteit van de titrant voor deze berekening.
		 * @param molariteit De molariteit waarmee gerekend zal worden (niet optioneel). 
		 * Als deze niet bepaald wordt, levert build een IllegalStateException.
		 * @throws IllegalArgumentException Als molariteit < 0. 
		 * @return De builder om chainen toe te staan.
		 */
		public Builder molariteitTitrant(double molariteit){
			if (molariteit < 0) throw new IllegalArgumentException("Molariteit < 0");
			mMolariteitTitrant = molariteit;
			return this;
		}
		
		/**
		 * Hiermee bepaal je het volume van de oplossing voor deze berekening.
		 * @param volume Het volume van de oplossing voor de berekening (niet optioneel).
		 * Als deze niet bepaald wordt, levert build een IllegalStateException.
		 * @throws IllegalArgumentException Als volume < 0. 
		 * @return De builder om chainen toe te staan.
		 */
		public Builder volumeOplossing(double volume){
			if (volume < 0) throw new IllegalArgumentException("Volume < 0");
			mVolumeOplossing = volume;
			return this;
		}
		
		/**
		 * Hiermee bepaal je de kleur die de grafiek zal proberen voor het tekenen.
		 * Momenteel is Color.ORANGE de standaard, dus optioneel.
		 * @param color De kleur voor de grafiek.
		 * @return De builder om chainen toe te staan.
		 * @throws NullPointerException Als color null is.
		 */
		public Builder color(Color color){
			if (color == null) throw new NullPointerException("Color = null");
			mColor = color;
			return this;
		}
		
		/**
		 * Levert een instantie van een toepasselijke berekening op.
		 * @return De berekening.
		 * @throws IllegalStateException Als molariteit oplossing, molariteit titrant of volume oplossing nooit is bepaald.
		 */
		public Berekening build(){
			if (mMolariteitOplossing == -1) throw new IllegalStateException("Molariteit oplossing is nooit bepaald");
			if (mMolariteitTitrant == -1) throw new IllegalStateException("Molariteit titrant is nooit bepaald");
			if (mVolumeOplossing == -1) throw new IllegalStateException("Volume oplossing is nooit bepaald");
			
			AbstractBerekening ber = null;
			if (mOplossing.isSterkZuur()){
				ber = new SterkeBerekening();				
			}
			else if (mOplossing.isSterkeBase()){
				ber = new SterkeBerekening();
			}
			else {
				int waardigheid = mTitrant.isBase() ? mOplossing.getWaardigheidZuur() : mOplossing.getWaardigheidBase();
				switch (waardigheid){
				case 1: {
					ber = new EenwaardigZwakkeBerekening();
					break;
				}
				case 2: {
					ber = new TweewaardigZwakkeBerekening();
					break;
				}
				case 3: {
					ber = new DriewaardigZwakkeBerekening();
					break;
				}
				}
			}
			ber.setColor(mColor);
			ber.setMolariteitOplossing(mMolariteitOplossing);
			ber.setMolariteitTitrant(mMolariteitTitrant);
			ber.setVolumeOplossing(mVolumeOplossing);
			ber.setTypeStof(mTitrant.isZuur() ? BASE : ZUUR);
			ber.setNaamOplossing(mOplossing.getNaam());
			
			if ((mOplossing.isZwakZuur()) || (mOplossing.isZwakkeBase())){
				if (mOplossing.isZuur()) {
					((ZwakkeBerekening)ber).setKz(mOplossing.getKzs()); // Mogelijk nog aanpassen
				}
				else {
					((ZwakkeBerekening)ber).setKz(AbstractBerekening.kbsToKzs(mOplossing.getKbs())); // Mogelijk nog aanpassen
				}
			}
			return ber;			
		}
	}


	/**
	 * Methode voor het berekenen van het pH vanuit de toegevoegde hoeveelheid titrant.
	 * @param titrant Hoeveelheid toegevoegde titrant in mL.
	 */
	public abstract double berekenVanuitTitrant(double titrant);



	/** 
	 * De kleur die voor deze grafiek bij voorkeur gebruikt wordt. Zal mogelijk niet gebruikt worden, indien er andere grafieken met eenzelfde kleur zijn.
	 * @uml.property  name="preferredColor"
	 */
	public Color getPreferredColor();
	
	/**
	 * Hiermee bepaal je de molariteit van de oplossing in de erlenmeyer.
	 * @param molariteit De molariteit van de oplossing.
	 */
	public void setMolariteitOplossing(double molariteit);
	
	/**
	 * Hiermee bepaal je de molariteit van de oplossing in de buret (dus de titrant).
	 * @param molariteit De molariteit van de titrant.
	 */
	public void setMolariteitTitrant(double molariteit);
	
	/**
	 * Hiermee bepaal je het volume van de oplossing in de erlenmeyer.
	 * @param volume Het volume van de oplossing in de erlenmeyer.
	 */
	public void setVolumeOplossing(double volume);
	
	/**
	 * Hiermee bepaal je het soort stof wat je gaat titreren, een zuur of een base
	 * @param type (aanroepbaar via berekening.ZUUR of berekening.BASE)
	 */
	public void setTypeStof(int type);
	
	public void setNaamOplossing(String naam);
	
	public double getMolariteitOplossing();
	
	public double getMolariteitTitrant();
	
	public double getVolumeOplossing();
	
	public String getNaamOplossing();
	
	public int getTypeTitrant();
	
	public interface Equivalentable{
		
		/**
		 * Geeft een array van hoeveelheid titrant waar een equivalentie punt te vinden is.
		 * @return
		 */
		public double[] getEquivalences();
	}
}
